package locusts.common.entities;

import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.vecmath.Vector2d;

import locusts.common.behaviours.Behaviour;
import locusts.lib.MathUtil;

/**
 * 
 */
public class Flock {

    private final List<Vehicle> boids;
    private Behaviour behaviour;

    public Flock(List<Vehicle> boids, Behaviour behaviour) {
        if (boids == null)
            throw new IllegalArgumentException();
        this.boids = boids;
        this.behaviour = behaviour;
    }

    public Flock(Behaviour behaviour) {
        this(new ArrayList<Vehicle>(), behaviour);
    }

    public Flock() {
        this(null);
    }

    public void setBehaviour(Behaviour behaviour) {
        this.behaviour = behaviour;
    }
    

    public void addBoid(double x, double y, double vx, double vy) {
        boids.add(new Vehicle(x, y, vx, vy, 0d, 0d));
    }

    public void populate(int n, Rectangle2D bounds) {
        final Random RAND = new Random();
        for (int i = 0; i < n; i++) {
            final double x = bounds.getMinX() + RAND.nextDouble() *
                    bounds.getWidth();
            final double y = bounds.getMinY() + RAND.nextDouble() *
                    bounds.getHeight();
            final double heading = RAND.nextDouble() * MathUtil.TWO_PI;
            final double vx = Math.cos(heading) * 10d;
            final double vy = Math.sin(heading) * 10d;

            addBoid(x, y, vx, vy);
        }
    }

    public List<Vehicle> getBoids() {
        return boids;
    }

    public void update(double time) {
        updateBehaviours(time);
        updatePositions(time);
    }

    public void updateBehaviours(double time) {

        if(behaviour == null)
            return;
        
        final double maxAcceleration = 100; // px/s/s

        Random rand = new Random();
        for (Vehicle boid : boids) {
            if (rand.nextDouble() > 0.1d)
                continue;

            Vector2d steering = behaviour.getSteering(boid);

            // If the vector is small then don't bother doing anything else
            // if it's zero it will cause divide by zero errors anyway
            if (steering.lengthSquared() < 1e-10d)
                continue;

            // max acceleration in pixels per second per second
            steering.normalize();
            steering.scale(maxAcceleration);

            boid.acceleration.x = steering.x;
            boid.acceleration.y = steering.y;

        }
    }

    public void updatePositions(double time) {
        final double momentum = 0.90d;
        final double tickMomentum = Math.pow(momentum, time);
        for (Vehicle b : boids) {
            b.velocity.scale(tickMomentum);
            b.velocity.x += b.acceleration.x * time;
            b.velocity.y += b.acceleration.y * time;
            b.position.x += b.velocity.x * time;
            b.position.y += b.velocity.y * time;
        }
    }
}
